﻿/* HEADER
 * ------
 * © 2009 by Salomon Zwecker 
 * modified by:
 * - 
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Shapes.Geometry
{
    /// <summary>
    /// The LineStrip class is a collection of connected lines
    /// </summary>
    public sealed class LineStrip : PointList<Vector2>, ILinedGeometry
    {
        /// <summary>
        /// Creates a new instance of LineStrip
        /// </summary>
        /// <param name="corners">the ordered List of corners of the LineStrip</param>
        [Obsolete("this constructor overload is only for backwards compatibility")]
        public LineStrip(List<Vector2> corners)
            : base(corners.ToArray())
        {
            FitBounding();
        }

        /// <summary>
        /// Creates a new instance of LineStrip
        /// </summary>
        /// <param name="corners">the ordered List of corners of the LineStrip</param>
        public LineStrip(params Vector2[] corners)
            : base(corners)
        {
            FitBounding();
        }

        /// <summary>
        /// connects the end of the LineStrip with the beginning
        /// </summary>
        public override void Close()
        {
            if (IsClosed)
                return;

            _Lines.Add(new Line(_Points[_Points.Count - 1], _Points[0]));
            IsClosed = true;

            FitBounding();
            CallOnChange();
        }

        internal override float GetDistanceToEdge(ref Vector2 point) 
        {
            return GetNearestLine(point, true).GetDistanceToEdge(point);
        }

        internal override Vector2 GetNearestPointOnEdge(ref Vector2 point) 
        {
            return GetNearestLine(point, true).GetNearestPointOnEdge(point);
        }

        internal override Vector2 GetPositionFromT(float t)
        {
            t = ( t) * _BorderLength;
            foreach (Line l in _Lines)
            {
                t -= l.BorderLength;
                if (t <= 0)
                {
                    return l.GetPositionFromEdgePath(1 + (t / l.BorderLength));
                }
            }
            return _Lines[0].StartPoint;
        }

        internal override float GetEdgePathValueFromPoint(ref Vector2 point)
        {
            Line l = GetNearestLine(point);

            float length = l.GetEdgePathValueFromPoint(ref point) * l.BorderLength;
            int idx = _Lines.IndexOf(l);
            for (int i = 0; i < idx; i++)
            {
                length += _Lines[i].BorderLength;
            }

            return length / _BorderLength;
        }
        internal override Vector2 GetTangent(ref Vector2 position)
        {
            Line l = GetNearestLine(position, true);
            return l.GetTangent(ref position);
        }

        internal override Vector2 GetNormal(ref Vector2 position)
        {
            Line l = GetNearestLine(position, true);
            return l.GetNormal(ref position);
        }
        /// <summary>
        /// Fits the bounding
        /// </summary>
        internal override void FitBounding()
        {
            Vector2 smallPos = new Vector2(float.MaxValue, float.MaxValue);
            Vector2 farPos = new Vector2(float.MinValue, float.MinValue);

            foreach (Vector2 p in _Points)
            {
                #region get smallest point

                if (p.X < smallPos.X)
                    smallPos.X = p.X;
                if (p.Y < smallPos.Y)
                    smallPos.Y = p.Y;

                #endregion
                #region get farest point

                if (p.X > farPos.X)
                    farPos.X = p.X;
                if (p.Y > farPos.Y)
                    farPos.Y = p.Y;

                #endregion
            }

            _BorderLength = 0;
            foreach (Line line in _Lines)
            {
                line.SetPoints(line.StartPoint - smallPos, line.EndPoint - smallPos);
                _BorderLength += line.BorderLength;
            }
            for(int i = 0; i < _Points.Count; i++)
            {
                _Points[i] -= smallPos;
            }


            _Transform._Position += smallPos;
            _Transform._Width = farPos.X - smallPos.X;
            _Transform._Height = farPos.Y - smallPos.Y;
            _Transform._IsDirty = true;
            _Transform.CallOnChange();
        }

        /// <summary>
        /// converts the given position to local coordinates
        /// </summary>
        /// <param name="globalPosition">the global position</param>
        /// <returns>the local position</returns>
        public override Vector2 GetLocalPositionOfPoint(Vector2 globalPosition)
        {
            return _Transform.TransformGlobalToLocal(globalPosition);
        }

        /// <summary>
        /// converts the given postion to global coordinates
        /// </summary>
        /// <param name="localPosition">the local position</param>
        /// <returns>the global position</returns>
        public override Vector2 GetGlobalPositionOfPoint(Vector2 localPosition)
        {
            return _Transform.TransformLocalToGlobal(localPosition);
        }

        /// <summary>
        /// Returns the point as a vector (only relevant for SplinePoints)
        /// </summary>
        /// <param name="point">the return point</param>
        /// <returns>the given point</returns>
        protected override Vector2 GetVectorOfPoint(Vector2 point)
        {
            return point;
        }
        /// <summary>
        /// converts the given position to local coordinates
        /// </summary>
        protected override void ToLocalCoordinates(ref Vector2 outPoint)
        {
            _Transform.TransformGlobalToLocal(ref outPoint);
        }

        /// <summary>
        /// converts the given postion to global coordinates
        /// </summary>
        protected override void ToGlobalCoordinates(ref Vector2 outPoint)
        {
            _Transform.TransformLocalToGlobal(ref outPoint);
        }

        /// <summary>
        /// Clones the LineStrip
        /// </summary>
        /// <returns>An object of the type LineStrip</returns>
        public override object Clone()
        {
            LineStrip ls = new LineStrip(_Points.ToArray())
                               {
                                   _Transform = (Transformation2D) _Transform.Clone()
                               };
            return ls;
        }

        /// <summary>
        /// Gets the enumeration type which is mapped to this kind of object
        /// </summary>
        /// <returns>the geometry typee of this object</returns>
        public override GeometryType GetGeometryType()
        {
            return GeometryType.LineStrip;
        }

        /// <summary>
        /// Iterates through all Lines of the object
        /// </summary>
        /// <returns>every line the object contains</returns>
        public IEnumerable<Line> GetLines()
        {
            foreach (Line l in _Lines)
            {
                yield return l;
            }
        }
        /// <summary>
        /// The global position of the first lines start point
        /// </summary>
        public Vector2 StartPoint
        {
            get { return Transform.TransformLocalToGlobal(_Lines[0].StartPoint); }
        }


        public int LineCount
        {
            get { return _Lines.Count; }
        }

        /// <summary>
        /// same as Colne() in this case.
        /// </summary>
        /// <returns>a line strip object</returns>
        public override LineStrip ToLineStrip()
        {
            return (LineStrip)this.Clone();
        }
    }
}
